package com.kexio.file.storage.impl;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.kexio.file.config.FileStorageProperties;
import com.kexio.file.storage.StorageFileInfo;
import com.kexio.file.storage.StorageProvider;
import com.kexio.file.storage.StorageResult;

import io.minio.BucketExistsArgs;
import io.minio.CopyObjectArgs;
import io.minio.CopySource;
import io.minio.GetObjectArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.errors.ErrorResponseException;
import io.minio.http.Method;

/**
 * MinIO 存储提供者
 * 
 * 基于 MinIO S3 兼容存储的文件存储实现
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Component
@ConditionalOnClass(name = "io.minio.MinioClient")
public class MinIOStorageProvider implements StorageProvider {
    
    private static final Logger log = LoggerFactory.getLogger(MinIOStorageProvider.class);
    
    @Autowired
    private FileStorageProperties storageProperties;
    
    private volatile MinioClient minioClient;
    
    /**
     * 获取 MinIO 客户端（懒加载）
     */
    private MinioClient getMinioClient() {
        if (minioClient == null) {
            synchronized (this) {
                if (minioClient == null) {
                    initializeMinioClient();
                }
            }
        }
        return minioClient;
    }
    
    /**
     * 初始化 MinIO 客户端
     */
    private void initializeMinioClient() {
        try {
            FileStorageProperties.MinioStorageConfig minioConfig = storageProperties.getMinio();
            
            MinioClient.Builder builder = MinioClient.builder()
                .endpoint(minioConfig.getEndpoint());
                
            if (StringUtils.hasText(minioConfig.getAccessKey()) && 
                StringUtils.hasText(minioConfig.getSecretKey())) {
                builder.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey());
            }
            
            minioClient = builder.build();
            
            log.info("MinIO 客户端初始化成功: endpoint={}", minioConfig.getEndpoint());
            
        } catch (Exception e) {
            log.error("MinIO 客户端初始化失败", e);
            throw new RuntimeException("MinIO 客户端初始化失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String getStorageType() {
        return "minio";
    }
    
    @Override
    public StorageResult storeFile(String bucketName, String objectKey, InputStream inputStream, 
                                  long contentLength, String contentType, Map<String, String> metadata) {
        try {
            MinioClient client = getMinioClient();
            
            // 构建上传参数
            PutObjectArgs.Builder builder = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .stream(inputStream, contentLength, -1);
                
            if (StringUtils.hasText(contentType)) {
                builder.contentType(contentType);
            }
            
            if (metadata != null && !metadata.isEmpty()) {
                builder.userMetadata(metadata);
            }
            
            // 执行上传
            client.putObject(builder.build());
            
            // 构建存储结果
            StorageResult result = new StorageResult();
            result.setBucketName(bucketName);
            result.setObjectKey(objectKey);
            result.setFileSize(contentLength);
            result.setContentType(contentType);
            result.setStorageUrl(buildStorageUrl(bucketName, objectKey));
            result.setMetadata(metadata);
            result.setSuccess(true);
            
            log.debug("文件上传成功: bucket={}, key={}, size={}", bucketName, objectKey, contentLength);
            
            return result;
            
        } catch (Exception e) {
            log.error("MinIO 文件上传失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("MinIO 文件上传失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public InputStream getFile(String bucketName, String objectKey) {
        try {
            MinioClient client = getMinioClient();
            
            return client.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build());
                
        } catch (Exception e) {
            log.error("MinIO 文件获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("MinIO 文件获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean deleteFile(String bucketName, String objectKey) {
        try {
            MinioClient client = getMinioClient();
            
            client.removeObject(RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build());
                
            log.debug("文件删除成功: bucket={}, key={}", bucketName, objectKey);
            return true;
            
        } catch (Exception e) {
            log.error("MinIO 文件删除失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public boolean fileExists(String bucketName, String objectKey) {
        try {
            MinioClient client = getMinioClient();
            
            client.statObject(StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build());
                
            return true;
            
        } catch (ErrorResponseException e) {
            if (e.errorResponse().code().equals("NoSuchKey")) {
                return false;
            }
            log.error("MinIO 文件存在性检查失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        } catch (Exception e) {
            log.error("MinIO 文件存在性检查失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public StorageFileInfo getFileInfo(String bucketName, String objectKey) {
        try {
            MinioClient client = getMinioClient();
            
            StatObjectResponse stat = client.statObject(StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build());
                
            StorageFileInfo fileInfo = new StorageFileInfo();
            fileInfo.setFileSize(stat.size());
            fileInfo.setContentType(stat.contentType());
            fileInfo.setLastModified(stat.lastModified().toLocalDateTime());
            fileInfo.setEtag(stat.etag());
            fileInfo.setMetadata(stat.userMetadata());
            fileInfo.setExists(true);
            
            return fileInfo;
            
        } catch (Exception e) {
            log.error("MinIO 文件信息获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("MinIO 文件信息获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedUploadUrl(String bucketName, String objectKey, 
                                           int expireMinutes, String contentType) {
        try {
            MinioClient client = getMinioClient();
            
            Map<String, String> reqParams = new HashMap<>();
            if (StringUtils.hasText(contentType)) {
                reqParams.put("content-type", contentType);
            }
            
            return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.PUT)
                .bucket(bucketName)
                .object(objectKey)
                .expiry(expireMinutes * 60)
                .extraQueryParams(reqParams)
                .build());
                
        } catch (Exception e) {
            log.error("MinIO 预签名上传URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("MinIO 预签名上传URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedDownloadUrl(String bucketName, String objectKey, int expireMinutes) {
        try {
            MinioClient client = getMinioClient();
            
            return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectKey)
                .expiry(expireMinutes * 60)
                .build());
                
        } catch (Exception e) {
            log.error("MinIO 预签名下载URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("MinIO 预签名下载URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean copyFile(String sourceBucket, String sourceKey, String destBucket, String destKey) {
        try {
            MinioClient client = getMinioClient();
            
            client.copyObject(CopyObjectArgs.builder()
                .bucket(destBucket)
                .object(destKey)
                .source(CopySource.builder()
                    .bucket(sourceBucket)
                    .object(sourceKey)
                    .build())
                .build());
                
            log.debug("文件复制成功: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey);
            return true;
            
        } catch (Exception e) {
            log.error("MinIO 文件复制失败: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey, e);
            return false;
        }
    }
    
    @Override
    public boolean createBucketIfNotExists(String bucketName) {
        try {
            MinioClient client = getMinioClient();
            
            // 检查存储桶是否存在
            boolean exists = client.bucketExists(BucketExistsArgs.builder()
                .bucket(bucketName)
                .build());
                
            if (!exists) {
                // 创建存储桶
                client.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
                    
                log.info("MinIO 存储桶创建成功: {}", bucketName);
            } else {
                log.debug("MinIO 存储桶已存在: {}", bucketName);
            }
            
            return true;
            
        } catch (Exception e) {
            log.error("MinIO 存储桶创建失败: {}", bucketName, e);
            return false;
        }
    }
    
    @Override
    public boolean verifyFileIntegrity(String bucketName, String objectKey, String expectedHash) {
        try {
            StorageFileInfo fileInfo = getFileInfo(bucketName, objectKey);
            String actualEtag = fileInfo.getEtag();
            
            // MinIO 的 ETag 通常是文件的 MD5 哈希值（但可能包含引号）
            if (actualEtag != null) {
                actualEtag = actualEtag.replaceAll("\"", "");
            }
            
            boolean isValid = expectedHash != null && expectedHash.equals(actualEtag);
            
            log.debug("文件完整性验证: bucket={}, key={}, expected={}, actual={}, valid={}", 
                     bucketName, objectKey, expectedHash, actualEtag, isValid);
                     
            return isValid;
            
        } catch (Exception e) {
            log.error("MinIO 文件完整性验证失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    /**
     * 构建存储URL
     */
    private String buildStorageUrl(String bucketName, String objectKey) {
        FileStorageProperties.MinioStorageConfig minioConfig = storageProperties.getMinio();
        String endpoint = minioConfig.getEndpoint();
        
        // 确保 endpoint 以 / 结尾
        if (!endpoint.endsWith("/")) {
            endpoint += "/";
        }
        
        return endpoint + bucketName + "/" + objectKey;
    }
}
